# Socket编程知识点
# Socket是网络编程的基础，用于在网络上发送和接收数据

# 1. Socket基本概念
# Socket是一种通信机制，允许不同计算机上的进程相互通信
# 它是网络编程的接口，支持TCP和UDP协议

# 2. Socket类型
# SOCK_STREAM: 流式套接字，用于TCP协议，提供可靠的、面向连接的通信
# SOCK_DGRAM: 数据报套接字，用于UDP协议，提供无连接的通信

# 3. TCP服务器端编程步骤
# 1) 创建socket对象
#    server_socket = socket.socket(family, type)
#    family: 地址族，如socket.AF_INET表示IPv4
#    type: 套接字类型，如socket.SOCK_STREAM表示TCP
#
# 2) 绑定地址和端口
#    server_socket.bind(address)
#    address: 元组形式(主机, 端口)，如('localhost', 8080)
#
# 3) 监听连接
#    server_socket.listen(backlog)
#    backlog: 整数，指定等待连接队列的最大长度
#
# 4) 接受客户端连接
#    client_socket, address = server_socket.accept()
#    返回值: (client_socket, address)元组
#    client_socket: 新的socket对象，用于与客户端通信
#    address: 客户端地址信息
#
# 5) 收发数据
#    data = client_socket.recv(bufsize)
#    bufsize: 整数，指定接收数据的最大字节数
#    client_socket.send(bytes)
#    bytes: 要发送的字节数据
#
# 6) 关闭连接
#    client_socket.close()
#    server_socket.close()

# 4. TCP客户端编程步骤
# 1) 创建socket对象
#    client_socket = socket.socket(family, type)
#    family: 地址族，如socket.AF_INET表示IPv4
#    type: 套接字类型，如socket.SOCK_STREAM表示TCP
#
# 2) 连接服务器
#    client_socket.connect(address)
#    address: 元组形式(主机, 端口)，如('localhost', 8080)
#
# 3) 收发数据
#    client_socket.send(bytes)
#    bytes: 要发送的字节数据
#    response = client_socket.recv(bufsize)
#    bufsize: 整数，指定接收数据的最大字节数
#
# 4) 关闭连接
#    client_socket.close()

# 5. UDP编程
# UDP是无连接的协议，不需要建立连接
# 发送数据: 
#    socket.sendto(data, address)
#    data: 要发送的字节数据
#    address: 元组形式(主机, 端口)
#
# 接收数据: 
#    data, address = socket.recvfrom(bufsize)
#    bufsize: 整数，指定接收数据的最大字节数
#    返回值: (data, address)元组
#    data: 接收到的字节数据
#    address: 发送方地址信息

# 6. 常用方法参数说明
# socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
#    family: 地址族，默认AF_INET(IPv4)
#    type: 套接字类型，默认SOCK_STREAM(TCP)
#    proto: 协议号，默认0表示自动选择
#
# bind(address)
#    address: 元组形式(主机, 端口)或字符串(Unix域套接字)
#
# listen(backlog=0)
#    backlog: 等待连接队列的最大长度
#
# accept()
#    返回值: (socket, address)元组
#
# connect(address)
#    address: 元组形式(主机, 端口)
#
# send(bytes, flags=0)
#    bytes: 要发送的字节数据
#    flags: 发送标志，默认0
#
# recv(bufsize, flags=0)
#    bufsize: 接收缓冲区大小(字节数)
#    flags: 接收标志，默认0
#
# close()
#    关闭套接字连接

# 7. 常用常量
# socket.AF_INET - IPv4地址族
# socket.SOCK_STREAM - TCP套接字
# socket.SOCK_DGRAM - UDP套接字

# 8. 异常处理
# socket.error - socket相关错误
# ConnectionResetError - 连接被重置
# TimeoutError - 连接超时

# 9. Socket编程注意事项
# 1) 需要正确处理异常
# 2) 及时关闭socket连接避免资源泄露
# 3) 注意数据编码和解码（bytes <-> string）
# 4) 合理设置超时时间
# 5) TCP是面向连接的可靠传输，UDP是无连接的不可靠传输

# 10. 应用场景
# 1) Web服务器和客户端
# 2) 聊天程序
# 3) 文件传输
# 4) 远程控制
# 5) 网络游戏

import socket
import threading
import time

def simple_tcp_server():
    """简单的TCP服务器"""
    # 创建TCP socket
    # socket.AF_INET: IPv4地址族
    # socket.SOCK_STREAM: 流式套接字(TCP)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 设置SO_REUSEADDR选项，允许地址重用
    # 避免"Address already in use"错误
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
    # 绑定地址和端口
    # host: 主机地址，'localhost'表示本机
    # port: 端口号，范围是0-65535，建议使用1024以上的端口
    host = 'localhost'
    port = 12345
    server_socket.bind((host, port))
    
    # 开始监听
    # 参数5: 最大等待连接数（队列大小）
    server_socket.listen(5)
    print(f"服务器启动，监听 {host}:{port}")
    
    try:
        # 接受客户端连接
        # 返回(client_socket, address)元组
        # client_socket: 与客户端通信的socket对象
        # address: 客户端地址信息(主机, 端口)
        client_socket, addr = server_socket.accept()
        print(f"连接来自: {addr}")
        
        # 接收数据
        # 参数1024: 缓冲区大小（字节数）
        # recv()是阻塞方法，会等待直到有数据到达
        data = client_socket.recv(1024)
        print(f"服务器收到数据: {data.decode('utf-8')}")
        
        # 发送数据
        # send()方法需要bytes类型参数，所以需要encode()
        response = "Hello from server"
        client_socket.send(response.encode('utf-8'))
        print(f"服务器发送数据: {response}")
        
        # 关闭连接
        client_socket.close()
    except Exception as e:
        print(f"服务器发生错误: {e}")
    finally:
        server_socket.close()
        print("服务器关闭")

def simple_tcp_client():
    """简单的TCP客户端"""
    # 等待服务器启动
    # time.sleep(1): 暂停1秒，确保服务器先启动
    time.sleep(1)
    
    # 创建TCP socket
    # socket.AF_INET: IPv4地址族
    # socket.SOCK_STREAM: 流式套接字(TCP)
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    try:
        # 连接服务器
        # host: 要连接的服务器主机地址
        # port: 要连接的服务器端口号
        host = 'localhost'
        port = 12345
        client_socket.connect((host, port))
        print(f"客户端连接到 {host}:{port}")
        
        # 发送数据
        # send()方法需要bytes类型参数，所以需要encode()
        # encode('utf-8'): 将字符串转换为bytes
        message = "Hello from client"
        client_socket.send(message.encode('utf-8'))
        print(f"客户端发送数据: {message}")
        
        # 接收数据
        # 参数1024: 缓冲区大小（字节数）
        # recv()是阻塞方法，会等待直到有数据到达
        # decode('utf-8'): 将bytes转换为字符串
        data = client_socket.recv(1024)
        print(f"客户端收到数据: {data.decode('utf-8')}")
        
    except Exception as e:
        print(f"客户端发生错误: {e}")
    finally:
        # 关闭连接
        client_socket.close()
        print("客户端关闭")

if __name__ == "__main__":
    print("Socket编程演示")
    print("1. 启动服务器线程")
    print("2. 启动客户端")
    print("-" * 30)
    
    # 创建并启动服务器线程
    # target: 线程要执行的函数
    # daemon=True: 设置为守护线程，主程序退出时自动退出
    server_thread = threading.Thread(target=simple_tcp_server)
    server_thread.daemon = True
    server_thread.start()
    
    # 运行客户端
    simple_tcp_client()
    
    # 等待服务器线程结束
    # timeout=2: 最多等待2秒
    server_thread.join(timeout=2)
    
    print("-" * 30)
    print("演示完成")